package com.finn.infrastructure.util;

/**
 * SnowFlake.
 *
 * @author Li ZeMin
 * @since 2020/7/26 1:50 AM
 */
public final class SnowFlake {

	private SnowFlake() {
	}

	/**
	 * 起始的时间戳.
	 */

	private static final long TWELVE = 12;

	private static final long START_STMP = 1480166465631L;

	/**
	 * 每一部分占用的位数.
	 */

	/**
	 * 序列号占用的位数.
	 */
	private static final long SEQUENCE_BIT = 12;

	/**
	 * 机器标识占用的位数.
	 */
	private static final long MACHINE_BIT = 5;

	/**
	 * 数据中心占用的位数.
	 */
	private static final long DATACENTER_BIT = 5;

	/**
	 * 每一部分的最大值.
	 */

	private static final long MAX_DATACENTER_NUM = -1L ^ (-1L << DATACENTER_BIT);

	private static final long MAX_MACHINE_NUM = -1L ^ (-1L << MACHINE_BIT);

	private static final long MAX_SEQUENCE = -1L ^ (-1L << SEQUENCE_BIT);

	/**
	 * 每一部分向左的位移.
	 */

	private static final long MACHINE_LEFT = SEQUENCE_BIT;

	private static final long DATACENTER_LEFT = SEQUENCE_BIT + MACHINE_BIT;

	private static final long TIMESTMP_LEFT = DATACENTER_LEFT + DATACENTER_BIT;

	/**
	 * 数据中心.
	 */
	private long datacenterId;

	/**
	 * 机器标识.
	 */
	private long machineId;

	/**
	 * 序列号.
	 */
	private long sequence = 0L;

	/**
	 * 上一次时间戳.
	 */
	private long lastStmp = -1L;

	public SnowFlake(long datacenterId, long machineId) {

		if (datacenterId > this.MAX_DATACENTER_NUM || datacenterId < 0) {

			throw new IllegalArgumentException("datacenterId can't be greater than MAX_DATACENTER_NUM or less than 0");

		}

		if (machineId > this.MAX_MACHINE_NUM || machineId < 0) {

			throw new IllegalArgumentException("machineId can't be greater than MAX_MACHINE_NUM or less than 0");

		}

		this.datacenterId = datacenterId;

		this.machineId = machineId;

	}

	/**
	 * 产生下一个ID.
	 * @return long id
	 */
	public synchronized long nextId() {

		long currStmp = getNewstmp();

		if (currStmp < this.lastStmp) {

			throw new RuntimeException("Clock moved backwards.  Refusing to generate id");

		}

		if (currStmp == this.lastStmp) {

			// 相同毫秒内，序列号自增

			this.sequence = (this.sequence + 1) & this.MAX_SEQUENCE;

			// 同一毫秒的序列数已经达到最大

			if (this.sequence == 0L) {

				currStmp = getNextMill();

			}

		}
		else {

			/**
			 * 不同毫秒内，序列号置为0.
			 */
			this.sequence = 0L;

		}

		this.lastStmp = currStmp;

		/**
		 * 时间戳部分.
		 */
		return (currStmp - this.START_STMP) << this.TIMESTMP_LEFT

				/**
				 * 数据中心部分.
				 */
				| this.datacenterId << this.DATACENTER_LEFT

				/**
				 * 机器标识部分.
				 */
				| this.machineId << this.MACHINE_LEFT

				/**
				 * 序列号部分.
				 */
				| this.sequence;

	}

	private long getNextMill() {

		long mill = getNewstmp();

		while (mill <= this.lastStmp) {

			mill = getNewstmp();

		}

		return mill;

	}

	private long getNewstmp() {

		return System.currentTimeMillis();

	}

	public static void main(String[] args) {

		SnowFlake snowFlake = new SnowFlake(2, 3);

		for (int i = 0; i < (1 << TWELVE); i++) {

			System.out.println(snowFlake.nextId());

		}

	}

}
